home *** CD-ROM | disk | FTP | other *** search
- /*****************************************************************************
- * pdb - routines for maintaining a database of performance information
- *
- * Porting Notes:
- *
- * ANSI C (including library routines specified by the standard) is
- * used throughout.
- *
- * The routines GetDBFileName() and GetDefaultMachineName() should be
- * modified when porting to non-UNIX systems.
- *
- * GetClock() should be modified when porting to non-SVR4 systems, or
- * to systems whose "double" type has fewer bits of mantissa than
- * specified by IEEE 754.
- *
- * History:
- *
- * 1.0 9/93 akin Written. See accompanying README for
- * rationale and examples.
- *****************************************************************************/
-
-
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/time.h> /* for gettimeofday, used by GetClock */
- #include "pdb.h"
-
-
-
- typedef struct HashNodeS
- {
- struct HashNodeS* next;
- char* machineName;
- char* applicationName;
- char* benchmarkName;
- double rate;
- char storage[1];
- } HashNodeT;
- static HashNodeT** HashTable = NULL;
- #define HASH_STEP(hash,c) (hash)+=(c)
- #define HASH_TABLE_SIZE 64
- #define HASH_MODULUS(hash) (hash)&=(HASH_TABLE_SIZE-1)
-
-
-
- static int Dirty = 0;
-
-
-
- #define LINE_MAX 1024
-
-
-
- static double ChooseRunTime (void);
- static pdbStatusT DumpHashTable (FILE* dbFile);
- static void FinalizeHashTable (void);
- static double GetClock (void);
- static void GetDBFileName (char* name);
- static void GetDefaultMachineName (char* name);
- static pdbStatusT InitializeHashTable (void);
- static pdbStatusT InsertHashNode (const char* machineName,
- int machineNameLength,
- const char* applicationName,
- int applicationNameLength,
- const char* benchmarkName,
- int benchmarkNameLength,
- double rate,
- unsigned hash);
- static pdbStatusT LoadHashTable (FILE* dbFile);
- static pdbStatusT LookupHashNode (HashNodeT** nodeP,
- const char* machineName,
- const char* applicationName,
- const char* benchmarkName,
- int createWhenMissing);
- static double WaitForTick (void);
-
-
-
- /*****************************************************************************
- * ChooseRunTime - select an appropriate runtime for benchmarks
- *****************************************************************************/
-
- static double
- ChooseRunTime(void)
- {
- register double start;
- register double finish;
- double runTime;
-
- start = GetClock();
-
- /* Wait for next tick: */
- while ((finish = GetClock()) == start)
- ;
-
- /* Run for 100 ticks, clamped to [0.5 sec, 5.0 sec]: */
- runTime = 100.0 * (finish - start);
- if (runTime < 0.5)
- runTime = 0.5;
- else if (runTime > 5.0)
- runTime = 5.0;
-
- return runTime;
- }
-
-
-
- /*****************************************************************************
- * DumpHashTable - write contents of hash table to database file
- *****************************************************************************/
-
- static pdbStatusT
- DumpHashTable(FILE* dbFile)
- {
- register int i;
- register HashNodeT* n;
-
- if (!HashTable)
- return PDB_NOT_OPEN;
-
- for (i = 0; i < HASH_TABLE_SIZE; ++i)
- for (n = HashTable[i]; n; n = n->next)
- fprintf(dbFile, "%s\t%s\t%s\t%g\n",
- n->machineName,
- n->applicationName,
- n->benchmarkName,
- n->rate);
-
- return PDB_NO_ERROR;
- }
-
-
-
- /*****************************************************************************
- * FinalizeHashTable - deallocate all storage used by the hash table
- *****************************************************************************/
-
- static void
- FinalizeHashTable(void)
- {
- register int i;
- register HashNodeT* h;
- register HashNodeT* next;
-
- if (!HashTable)
- return;
-
- for (i = HASH_TABLE_SIZE - 1; i >= 0; --i)
- for (h = HashTable[i]; h; h = next)
- {
- next = h->next;
- free(h);
- }
-
- free(HashTable);
- HashTable = NULL;
- }
-
-
-
- /*****************************************************************************
- * GetClock - get current time (expressed in microseconds)
- *****************************************************************************/
-
- static double
- GetClock(void)
- {
- struct timeval t;
-
- gettimeofday(&t);
-
- return (double) t.tv_sec + (double) t.tv_usec * 1E-6;
- }
-
-
-
- /*****************************************************************************
- * GetDBFileName - get full pathname of performance database file
- *****************************************************************************/
-
- static void
- GetDBFileName(char* name)
- {
- char* home;
-
- if (home = getenv("HOME"))
- strcpy(name, home);
- else
- name[0] = '\0';
- strcat(name, "/.pdb");
- }
-
-
-
- /*****************************************************************************
- * GetDefaultMachineName - return name of "current" machine
- *****************************************************************************/
-
- static void
- GetDefaultMachineName(char* name)
- {
- char* display;
-
- if (display = getenv("DISPLAY"))
- strcpy(name, display);
- else
- strcpy(name, ":0");
- }
-
-
-
- /*****************************************************************************
- * InitializeHashTable - allocate memory for hash table and initialize it
- *****************************************************************************/
-
- static pdbStatusT
- InitializeHashTable(void)
- {
- register int i;
-
- HashTable = (HashNodeT**)
- malloc(HASH_TABLE_SIZE * sizeof(HashNodeT*));
- if (!HashTable)
- return PDB_OUT_OF_MEMORY;
-
- for (i = HASH_TABLE_SIZE - 1; i >= 0; --i)
- HashTable[i] = NULL;
-
- return PDB_NO_ERROR;
- }
-
-
-
- /*****************************************************************************
- * InsertHashNode - place key and data in a selected bucket of the hash table
- *
- * Note: Does not check for duplicates.
- * String length arguments *include* the zero byte at the end of the
- * string; e.g. "abc" would have length 4.
- *****************************************************************************/
-
- static pdbStatusT
- InsertHashNode
- (
- const char* machineName,
- int machineNameLength,
- const char* applicationName,
- int applicationNameLength,
- const char* benchmarkName,
- int benchmarkNameLength,
- double rate,
- unsigned hash
- )
- {
- register HashNodeT* n;
-
- if (!HashTable)
- return PDB_NOT_OPEN;
-
- n = (HashNodeT*) malloc(sizeof(HashNodeT) + machineNameLength
- + applicationNameLength + benchmarkNameLength);
- if (!n)
- return PDB_OUT_OF_MEMORY;
-
- n->machineName = n->storage;
- memcpy(n->machineName, machineName, machineNameLength);
- n->applicationName = n->machineName + machineNameLength;
- memcpy(n->applicationName, applicationName, applicationNameLength);
- n->benchmarkName = n->applicationName + applicationNameLength;
- memcpy(n->benchmarkName, benchmarkName, benchmarkNameLength);
- n->rate = rate;
-
- n->next = HashTable[hash];
- HashTable[hash] = n;
-
- return PDB_NO_ERROR;
- }
-
-
-
- /*****************************************************************************
- * LoadHashTable - load hash table with contents of performance database file
- *****************************************************************************/
-
- static pdbStatusT
- LoadHashTable(FILE* dbFile)
- {
- char line[LINE_MAX];
- pdbStatusT error = PDB_NO_ERROR;
-
- if (!HashTable)
- return PDB_NOT_OPEN;
-
- while (fgets(line, sizeof(line), dbFile))
- {
- char* machineName;
- char* applicationName;
- char* benchmarkName;
- int machineNameLength;
- int applicationNameLength;
- int benchmarkNameLength;
- double rate;
- register char* p;
- register int c;
- register unsigned hash;
-
-
- /* We use open code here, to minimize app startup time... */
-
- p = line;
- c = *p++;
- hash = 0;
-
- /* Skip whitespace before machine name: */
- while (c == ' ' || c == '\t')
- c = *p++;
- if (c == '\n')
- {
- error |= PDB_SYNTAX_ERROR;
- continue;
- }
-
- /* Scan machine name, get length and hash value: */
- machineName = p - 1;
- while (c != ' ' && c != '\t' && c != '\n')
- {
- HASH_STEP(hash, c);
- c = *p++;
- }
- p[-1] = '\0';
- machineNameLength = p - machineName; /* includes '\0' */
-
- /* Skip whitespace before application name: */
- while (c == ' ' || c == '\t')
- c = *p++;
- if (c == '\n')
- {
- error |= PDB_SYNTAX_ERROR;
- continue;
- }
-
- /* Scan application name, get length and hash: */
- applicationName = p - 1;
- while (c != ' ' && c != '\t' && c != '\n')
- {
- HASH_STEP(hash, c);
- c = *p++;
- }
- p[-1] = '\0';
- applicationNameLength = p - applicationName;
-
- /* Skip whitespace before benchmark name: */
- while (c == ' ' || c == '\t')
- c = *p++;
- if (c == '\n')
- {
- error |= PDB_SYNTAX_ERROR;
- continue;
- }
-
- /* Scan benchmark name, get length and hash: */
- benchmarkName = p - 1;
- while (c != ' ' && c != '\t' && c != '\n')
- {
- HASH_STEP(hash, c);
- c = *p++;
- }
- p[-1] = '\0';
- benchmarkNameLength = p - benchmarkName;
-
- /* Finally, get the rate: */
- rate = strtod(p, NULL);
- if (rate <= 0.0)
- error |= PDB_SYNTAX_ERROR; /* probably */
-
-
- HASH_MODULUS(hash);
-
-
- /* Note that we don't weed out any duplicates here... */
-
- error |= InsertHashNode(
- machineName, machineNameLength,
- applicationName, applicationNameLength,
- benchmarkName, benchmarkNameLength,
- rate, hash);
- }
-
- return error;
- }
-
-
-
- /*****************************************************************************
- * LookupHashNode - find key/data node in hash table, or insert it if needed
- *****************************************************************************/
-
- static pdbStatusT
- LookupHashNode
- (
- HashNodeT** nodeP,
- const char* machineName,
- const char* applicationName,
- const char* benchmarkName,
- int createWhenMissing
- )
- {
- char defaultMachineName[LINE_MAX];
- register const char* p;
- register int c;
- register unsigned hash;
- register HashNodeT* node;
- int machineNameLength;
- int applicationNameLength;
- int benchmarkNameLength;
- pdbStatusT error;
-
- if (!HashTable)
- return PDB_NOT_OPEN;
-
- if (!machineName)
- {
- GetDefaultMachineName(defaultMachineName);
- machineName = defaultMachineName;
- }
-
- hash = 0;
-
- for (p = machineName, c = *p++; c; c = *p++)
- HASH_STEP(hash, c);
- machineNameLength = p - machineName; /* includes '\0' at end */
- for (p = applicationName, c = *p++; c; c = *p++)
- HASH_STEP(hash, c);
- applicationNameLength = p - applicationName;
- for (p = benchmarkName, c = *p++; c; c = *p++)
- HASH_STEP(hash, c);
- benchmarkNameLength = p - benchmarkName;
-
- HASH_MODULUS(hash);
-
- for (node = HashTable[hash]; node; node = node->next)
- if (!strcmp(node->machineName, machineName)
- && !strcmp(node->applicationName, applicationName)
- && !strcmp(node->benchmarkName, benchmarkName))
- {
- *nodeP = node;
- return PDB_NO_ERROR;
- }
-
- if (createWhenMissing)
- {
- error = InsertHashNode(
- machineName, machineNameLength,
- applicationName, applicationNameLength,
- benchmarkName, benchmarkNameLength,
- 0.0, hash);
- *nodeP = HashTable[hash];
- return error;
- }
- else
- return PDB_NOT_FOUND;
- }
-
-
-
- /*****************************************************************************
- * WaitForTick - wait for beginning of next system clock tick; return the time
- *****************************************************************************/
-
- static double
- WaitForTick(void)
- {
- register double start;
- register double current;
-
- start = GetClock();
-
- /* Wait for next tick: */
- while ((current = GetClock()) == start)
- ;
-
- /* Start timing: */
- return current;
- }
-
-
-
- /*****************************************************************************
- * pdbClose - write perf data to database file if necessary, then clean up
- *****************************************************************************/
-
- pdbStatusT
- pdbClose(void)
- {
- pdbStatusT error = PDB_NO_ERROR;
- char dbFileName[FILENAME_MAX];
- FILE* dbFile;
-
- if (!HashTable)
- return PDB_NOT_OPEN;
-
- if (Dirty)
- {
- GetDBFileName(dbFileName);
-
- if (dbFile = fopen(dbFileName, "w"))
- {
- error = DumpHashTable(dbFile);
- fclose(dbFile);
- }
- else
- error = PDB_CANT_WRITE;
-
- Dirty = 0;
- }
-
- FinalizeHashTable();
-
- return error;
- }
-
-
-
- /*****************************************************************************
- * pdbMeasureRate - measure number of caller's operations performed per second
- *****************************************************************************/
-
- pdbStatusT
- pdbMeasureRate
- (
- pdbCallbackT initialize,
- pdbCallbackT operation,
- pdbCallbackT finalize,
- double* rate
- )
- {
- register double runTime;
- register long reps;
- register long i;
- double finalizeTime;
- register double start;
- register double current;
- double overhead;
- double shortRunTime;
-
-
- if (!operation)
- {
- *rate = 0.0;
- return PDB_NO_ERROR;
- }
-
-
- /* Select a run time that's appropriate for our timer resolution: */
- runTime = ChooseRunTime();
- shortRunTime = 0.5 * runTime;
-
-
- /* Measure approximate overhead for finalization and timing routines: */
- if (initialize)
- (*initialize)();
- reps = 0;
- start = WaitForTick();
- do
- {
- if (finalize)
- (*finalize)();
- ++reps;
- } while ((current = GetClock()) < start + shortRunTime);
- overhead = (current - start) / (double) reps;
-
-
- /*
- * Measure successively larger batches of operations until we find
- * one that's long enough to meet our runtime target:
- */
- reps = 1;
- for (;;)
- {
- if (initialize)
- (*initialize)();
-
- start = WaitForTick();
-
- for (i = reps; i > 0; --i)
- (*operation)();
-
- if (finalize)
- (*finalize)();
-
- current = GetClock();
- if (current >= start + runTime + overhead)
- break;
-
- /* Try to reach runtime target in one fell swoop: */
- if (current > start)
- reps *= 0.5 + runTime / (current - start - overhead);
- else
- reps *= 2;
- }
-
- /* Subtract overhead to determine the final operation rate: */
- *rate = (double) reps / (current - start - overhead);
- return PDB_NO_ERROR;
- }
-
-
-
- /*****************************************************************************
- * pdbOpen - open perf database file, load contents into memory
- *****************************************************************************/
-
- pdbStatusT
- pdbOpen(void)
- {
- pdbStatusT error;
- char dbFileName[FILENAME_MAX];
- FILE* dbFile;
-
- if (HashTable)
- return PDB_ALREADY_OPEN;
-
- if (error = InitializeHashTable())
- return error;
-
- /* If the database file can be read, load its contents: */
- GetDBFileName(dbFileName);
- if (dbFile = fopen(dbFileName, "r"))
- {
- error = LoadHashTable(dbFile);
- fclose(dbFile);
- }
-
- /* The database is "clean" unless pdbWriteRate() is called: */
- Dirty = 0;
-
- return error;
- }
-
-
-
- /*****************************************************************************
- * pdbReadRate - return performance for a given machine, app, and benchmark
- *****************************************************************************/
-
- pdbStatusT
- pdbReadRate
- (
- const char* machineName,
- const char* applicationName,
- const char* benchmarkName,
- double* rate
- )
- {
- HashNodeT* node;
- pdbStatusT error;
-
- error = LookupHashNode(&node, machineName, applicationName,
- benchmarkName, 0); /* don't create node if it's not present */
- if (!error)
- *rate = node->rate;
-
- return error;
- }
-
-
-
- /*****************************************************************************
- * pdbWriteRate - save performance data for a given machine, app, and benchmark
- *****************************************************************************/
-
- pdbStatusT
- pdbWriteRate
- (
- const char* machineName,
- const char* applicationName,
- const char* benchmarkName,
- const double rate
- )
- {
- HashNodeT* node;
- pdbStatusT error;
-
- Dirty = 1;
-
- error = LookupHashNode(&node, machineName, applicationName,
- benchmarkName, 1); /* create node if it's not already in table */
- if (!error)
- node->rate = rate;
-
- return error;
- }
-